package com.core.utils;

import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.List;

import javax.tools.Diagnostic;
import javax.tools.DiagnosticCollector;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.SimpleJavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;

import org.apache.log4j.Logger;

import com.core.script.ScriptClassLoader;
/**
 * 脚本工具类
 * 编译java文件成class
 * 加载class文件
 * @author King
 *
 */
public class ScriptUtils 
{
	
	private Logger log = Logger.getLogger(ScriptUtils.class);
	
	private static ScriptUtils instance;
	
	private URLClassLoader parentClassLoader;

	private String classpath;

	private static Object obj = new Object();
	
	public ScriptUtils() {
		this.parentClassLoader = (URLClassLoader) this.getClass()
				.getClassLoader();
		this.buildClassPath();
		this.init();
	}
	
	private void buildClassPath() {
		this.classpath = null;
		StringBuilder sb = new StringBuilder();
		for (URL url : this.parentClassLoader.getURLs()) {
			String p = url.getFile();
			sb.append(p).append(File.pathSeparator);
		}
		this.classpath = sb.toString();
	}
	
	public static ScriptUtils getInstance()
	{
		if(instance==null)
		{
			synchronized (obj)
			{
				if(instance==null)
				{
					instance = new ScriptUtils();
				}
			}
		}
		return instance;
	}
	
	private void init(){
		File file = new File("bin");
		if(!file.exists()){
			file.mkdir();
		}
	}
	
	
	
	/**
	 * 编译java代码为.class
	 * @param name name 包名.类名(无需class和.java后缀)
	 * @param code java的 utf-8 代码字符串
	 * @return true 编译成功
	 * @throws IOException
	 */
	public boolean compilerJavaCode(String name, String code) throws IOException
	{
		JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
		DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<JavaFileObject>();
		StandardJavaFileManager fileManager = compiler.getStandardFileManager(
				diagnostics, null, null);
		
		JavaFileObject jfile = new JavaSourceFromString(name, code);
		List<JavaFileObject> jfiles = new ArrayList<JavaFileObject>();
		jfiles.add(jfile);
		List<String> options = new ArrayList<String>();

		options.add("-encoding");
		options.add("UTF-8");
		options.add("-classpath");
		options.add(this.classpath);
		options.add("-d");
		options.add("bin");

		JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager,
				diagnostics, options, null, jfiles);

		boolean success = task.call();
		
		fileManager.close();
		if(!success)
		{
			String error = "";
			for (Diagnostic<?> diagnostic : diagnostics.getDiagnostics()) {
				error = error + compilePrint(diagnostic);
			}
			log.error(error);
		}
		return success;
	}
	
	/**
	 * 动态加载class
	 * @param name 包名.类名(无需class和.java后缀)
	 * @param code
	 * @return
	 * @throws ClassNotFoundException
	 */
	public Class<?> loadClass(String name) throws ClassNotFoundException
	{
		boolean reload = false;
		try{
			Class<?> c = Class.forName(name);
			if(c!=null){
				reload = true;
			}
		}catch (Exception e) {}
		ScriptClassLoader loader = new ScriptClassLoader();
		if(reload)
		{
			return loader.loadScriptClass(name);
		}else{
			//可加载外部class
			return loader.loadScriptClass(name); 
//			return Class.forName(name);
		}
	}
	
	
	/**
	 * 编译错误打印
	 * @param diagnostic
	 * @return
	 */
	private String compilePrint(Diagnostic<?> diagnostic) {
		StringBuffer res = new StringBuffer();
		res.append("Code:[" + diagnostic.getCode() + "]\n");
		res.append("Kind:[" + diagnostic.getKind() + "]\n");
		res.append("Position:[" + diagnostic.getPosition() + "]\n");
		res.append("Start Position:[" + diagnostic.getStartPosition() + "]\n");
		res.append("End Position:[" + diagnostic.getEndPosition() + "]\n");
		res.append("Source:[" + diagnostic.getSource() + "]\n");
		res.append("Message:[" + diagnostic.getMessage(null) + "]\n");
		res.append("LineNumber:[" + diagnostic.getLineNumber() + "]\n");
		res.append("ColumnNumber:[" + diagnostic.getColumnNumber() + "]\n");
		return res.toString();
	}
	
	private class JavaSourceFromString extends SimpleJavaFileObject {

		private String code;
		
		public JavaSourceFromString(String name, String code) {
			super(URI.create("string:///" + name.replace('.', '/')
					+ Kind.SOURCE.extension), Kind.SOURCE);
			this.code = code;
		}
		 @Override
         public CharSequence getCharContent(boolean ignoreEncodingErrors) {
             return code;
         }
	}
}
